Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Erc4626 extension #465

Open
wants to merge 34 commits into
base: main
Choose a base branch
from

Conversation

Ifechukwudaniel
Copy link

@Ifechukwudaniel Ifechukwudaniel commented Dec 19, 2024

Resolves #356

PR Checklist

  • Tests
  • Documentation
  • Changelog

Copy link

netlify bot commented Dec 19, 2024

Deploy Preview for contracts-stylus canceled.

Name Link
🔨 Latest commit 8e4a720
🔍 Latest deploy log https://app.netlify.com/sites/contracts-stylus/deploys/676e8fe70a013b0008a1be33

@Ifechukwudaniel
Copy link
Author

I just stared this

@0xNeshi
Copy link
Collaborator

0xNeshi commented Dec 19, 2024

I'll convert this to a draft, so that the team does not invest time reviewing a WIP

@0xNeshi 0xNeshi marked this pull request as draft December 19, 2024 07:38
@0xNeshi 0xNeshi self-requested a review December 19, 2024 09:26
@Ifechukwudaniel
Copy link
Author

@0xNeshi
The Solidity implementation uses safe transfer

SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);

In the Solidity implementation, SafeERC20 is a utility library. However, in the Stylus implementation, SafeTransfer is a contract. What do you think

@0xNeshi
Copy link
Collaborator

0xNeshi commented Dec 19, 2024

@0xNeshi The Solidity implementation uses safe transfer

SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);

In the Solidity implementation, SafeERC20 is a utility library. However, in the Stylus implementation, SafeTransfer is a contract. What do you think

See how VestingWallet handles this

@Ifechukwudaniel
Copy link
Author

Should I implement something like mulDiv for U256 MATH?

function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
        return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
    }

@0xNeshi

@Ifechukwudaniel Ifechukwudaniel marked this pull request as ready for review December 24, 2024 00:09
@Ifechukwudaniel
Copy link
Author

Should I add the security concerns like docs erc4626

@0xNeshi
Copy link
Collaborator

0xNeshi commented Dec 27, 2024

Should I add the security concerns like docs erc4626

I think that would be beneficial.

@qalisander @bidzyyys

Copy link
Collaborator

@0xNeshi 0xNeshi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good first draft, but there's still much work to be done before the PR is deemed "ready for review".

@@ -13,6 +13,7 @@ members = [
"examples/erc1155",
"examples/erc1155-metadata-uri",
"examples/erc1155-supply",
"examples/erc4262",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"examples/erc4262",
"examples/erc4626",

it happens :D

Rename the example directory too

@@ -13,6 +13,7 @@ members = [
"examples/erc1155",
"examples/erc1155-metadata-uri",
"examples/erc1155-supply",
"examples/erc4262",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the same member to default-members.

sol!(
#[sol(rpc)]
contract Erc4626 {
function asset() public view returns (address);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

format Solidity code

function totalAssets() public view returns (uint256);
function convertToShares(uint256 assets) public view returns (uint256);
function convertToAssets(uint256 shares) public view returns (uint256);
function maxMint(address) public view returns (uint256);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function maxMint(address) public view returns (uint256);
function maxMint(address receiver) public view returns (uint256);

nit: align function signatures - all functions should list the names of their arguments

Comment on lines +33 to +34
function redeem(uint256 shares, address receiver) public returns (uint256);
function withdraw(uint256 assets, address receiver) public returns (uint256);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both of these are missing the owner parameter


[dependencies]
openzeppelin-stylus.workspace = true
alloy-primitives = { workspace = true, features = ["tiny-keccak"] }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need the tiny-keccak feature?

Comment on lines +8 to +16
constructor(address asset_) {
(bool success, uint8 assetDecimals) = _tryGetAssetDecimals();
_underlyingDecimals = success ? assetDecimals : 18;
_asset = asset_;
}

function _tryGetAssetDecimals() private pure returns (bool ok, uint8 assetDecimals) {
return (true, 0);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bidzyyys @qalisander I think the example should contain the whole logic here:

    constructor(IERC20 asset_) {
        (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
        _underlyingDecimals = success ? assetDecimals : 18;
        _asset = asset_;
    }

    /**
     * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
     */
    function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool ok, uint8 assetDecimals) {
        (bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
            abi.encodeCall(IERC20Metadata.decimals, ())
        );
        if (success && encodedDecimals.length >= 32) {
            uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
            if (returnedDecimals <= type(uint8).max) {
                return (true, uint8(returnedDecimals));
            }
        }
        return (false, 0);
    }

This should also be included in the docs as a note for lib users.


#[public]
#[inherit(Erc20, Erc20Metadata, Erc4626)]
impl Erc4262xample {
Copy link
Collaborator

@0xNeshi 0xNeshi Dec 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +52 to +59



#[allow(missing_docs)]
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

#[allow(missing_docs)]
event Withdraw(address indexed sender,address indexed receiver,ddress indexed owner,uint256 assets, uint256 shares);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Format the Solidity code.
  2. Remove unnecessary empty lines

async fn error_when_exceeded_max_redeem(
alice: Account,
bob: Account,
) -> Result<()> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement e2e tests, and ensure they pass successfully.

CHANGELOG.md Outdated Show resolved Hide resolved
@@ -0,0 +1,46 @@
= ERC-4262

https://eips.ethereum.org/EIPS/eip-4626[ERC-4626] is an extension of xref:erc20.adoc[ERC-20] that proposes a standard interface for token vaults. This standard interface can be used by widely different contracts (including lending markets, aggregators, and intrinsically interest bearing tokens), which brings a number of subtleties. Navigating these potential issues is essential to implementing a compliant and composable token vault.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Response to #465 (comment):

Should I add the security concerns like docs erc4626

Yes 👍


#[public]
#[inherit(Erc20, Erc20Metadata, Erc4626)]
impl Erc4262xample {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type Error: Into<alloc::vec::Vec<u8>>;

/// Returns the address of the underlying asset that the vault manages.
fn asset(&self) -> Address;
Copy link
Collaborator

@0xNeshi 0xNeshi Dec 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Double check all functions' docs for missing arguments.
  2. Ensure your docs follow the existing convention, see other contracts for examples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature]: ERC4626 extension
2 participants